package com.tianji.learning.service.impl;

import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.po.PointsRecord;
import com.tianji.learning.domain.vo.PointsStatisticsVO;
import com.tianji.learning.enums.PointsRecordType;
import com.tianji.learning.mapper.PointsRecordMapper;
import com.tianji.learning.service.IPointsRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * <p>
 * 学习积分记录，每个月底清零 服务实现类
 * </p>
 *
 * @author fx
 * @since 2024-08-29
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PointsRecordServiceImpl extends ServiceImpl<PointsRecordMapper, PointsRecord> implements IPointsRecordService {
    private final StringRedisTemplate redisTemplate;

    /**
     * 01 添加积分记录（day07-随堂-03）
     *
     * @param userId
     * @param points
     * @param type
     */
    @Override
    public void addPointsRecord(Long userId, Integer points, PointsRecordType type) {

        // 1 判断该type积分是否有上限 没有上限，直接增加，有上限，判断+处理后增加
        int maxPoints = type.getMaxPoints();
        LocalDateTime now = LocalDateTime.now();
        if (maxPoints > 0) {
            // 2 有上限的情况
            // 2.1 获取【用户】【当天】已经获得的【积分类型】及对应的【积分】
            LocalDateTime begin = DateUtils.getDayStartTime(now);
            LocalDateTime end = DateUtils.getDayEndTime(now);
            int currentPoints = queryUserPointsByTypeAndDate(userId, type, begin, end);
            // 2.2 判断是否已达上限，如果已达上限，直接返回
            if (currentPoints >= maxPoints) {
                log.error("今日 {} 类型积分已达上限", type);
                return;
            }
            // 2.3 未达上限，如果【本次积分】+【已获得积分】>【积分上限】

            if (currentPoints + points > maxPoints) {
                log.info("增加类型： {}， 积分：{} 后，超过积分上限，故本次增加积分调整为：{}", type, points, maxPoints - currentPoints);
                points = maxPoints - currentPoints;
            }
        }
        // 3 保存积分记录
        PointsRecord pointsRecord
                = new PointsRecord()
                .setUserId(userId)
                .setPoints(points)
                .setType(type);
        this.save(pointsRecord);

        // 4 TODO 更新积分 到 redis的积分排行表zset中（day08-随堂-01）
        // 4.1 组装key
        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX + now.format(DateUtils.DATE_SUFFIX_FORMATTER);
        redisTemplate.opsForZSet().incrementScore(key, userId.toString(), points);

    }

    /**
     * 02 查询当日积分记录（day07-随堂-03）
     * 积分详情 + 积分上限
     *
     * @return
     */
    @Override
    public List<PointsStatisticsVO> queryTodayPointsRecord() {
        // 1 获取查询条件
        // 查询时间
        LocalDateTime begin = DateUtils.getDayStartTime(LocalDateTime.now());
        LocalDateTime end = DateUtils.getDayEndTime(LocalDateTime.now());
        // 2 查询 各类型 积分总和
        List<PointsRecord> list = this.query()
                .select("type,sum(points) as points")
                .eq("user_id", UserContext.getUser())
                .between("create_time", begin, end)
                .groupBy("type")
                .list();
        if (ObjectUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        // 3 循环分组封装成vo
        List<PointsStatisticsVO> voList = new ArrayList<>();
        for (PointsRecord record : list) {
            PointsStatisticsVO vo = new PointsStatisticsVO();
            vo.setPoints(record.getPoints());
            vo.setMaxPoints(record.getType().getMaxPoints());
            vo.setType(record.getType().getDesc());
            voList.add(vo);
        }

        // 返回voList
        return voList;

    }

    /**
     * tool-01 查询 用户 当天 该类型 积分总数 （day07-随堂-03）
     *
     * @param userId
     * @param type
     * @param begin
     * @param end
     * @return
     */
    private int queryUserPointsByTypeAndDate(Long userId, PointsRecordType type, LocalDateTime begin, LocalDateTime end) {
        PointsRecord record = this.query()
                .select("sum(points) as points")
                .eq("user_id", userId)
                .eq("type", type)
                .between("create_time", begin, end)
                .one();
        /*if (ObjectUtils.isEmpty(record)|| ObjectUtils.isNull(record.getPoints()))
        {
            return 0;
        }
        return record.getPoints();*/
        return ObjectUtils.isEmpty(record) || ObjectUtils.isEmpty(record.getPoints()) ? 0 : record.getPoints();
    }
}
